#pragma once

#include "fixed_point_helper.h"
#include "vertex_buffer.h"
#include "triangle_renderer_software.h"
#include "renderer.h"
#include "render_job.h"

#include <iostream>


namespace krryn{
namespace scene{
	template
	<
		typename _Vertex
	>
	class vertex_buffer<system::software_tag, _Vertex> : public hardware_buffer{
	public:
		static const bool m_IsVertexBuffer = true;

		typedef _Vertex vertex_type;
		typedef typename _Vertex::description_type description_type;
		typedef krryn::core::refc_ptr<vertex_buffer> ptr;
		typedef index_buffer<system::software_tag> index_buffer_type;

		typedef int type;

		typedef type* iterator;
		typedef type const* const_iterator;
		
		vertex_buffer(system::renderer<system::software_tag> &a_Renderer, size_t a_Size) 
			: m_Size(a_Size), m_Renderer(a_Renderer){
			// allocate a fixed size vector for performance.
			// this also guarantees us that the m_Current iterator will never
			// invalid as long as we don't have to resize the vector 
			// (which we don't because operator += does bounds checking).

			size_t l_Size = a_Size * (description_type::m_Size + 1);
			m_Vertices = new type[l_Size];
			m_Current = m_Vertices;
			m_End = m_Vertices + l_Size;
		}

		~vertex_buffer(){
			delete[] m_Vertices;
		}

		void lock(lock_type a_Type){
			hardware_buffer::lock(a_Type);
		}

		void unlock(){
			hardware_buffer::unlock();
		}

		iterator begin(){ return m_Vertices; }
		const_iterator begin() const{ return m_Vertices; }

		iterator end(){ return m_Vertices + a_Size * description_type::m_Size; }
		const_iterator end() const{ return m_Vertices + a_Size * description_type::m_Size; }

		size_t size() const{
			return m_Size;
		}

		void draw(){}

		void draw_indexed(const index_buffer_type &a_Buffer){}

		void operator +=(const _Vertex &a_Vertex){
			if(locked() ^ write){
				throw scene_exception("Buffer must be write-locked for insertion");
			}

			if(m_Current >= m_End){
				throw scene_exception("Buffer out of bounds");
			}

			float l_VertexData[description_type::m_Size];

			a_Vertex.copy_into(
				(char*)l_VertexData, // begin
				(char*)(l_VertexData + description_type::m_Size) // end
			);

			/// convert each item to fixed point individually
			for(size_t i = 0; i < description_type::m_Size; i++){
				*m_Current++ = cast_to<type>(l_VertexData[i]);

				/// add a W component
				if(i == 2) *m_Current++ = cast_to<type>(1.f);
			}
		}

	private:
		// current and starting position of this buffer
		type *m_Current;
		type *m_Vertices;
		type *m_End;

		size_t m_Size;

		system::renderer<system::software_tag> &m_Renderer;
	};
}
}
